using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using Microsoft.SqlServer.Dts.Design;
using Microsoft.SqlServer.Dts.Pipeline.Design;
using Microsoft.SqlServer.Dts.Runtime;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using System.Windows.Forms;
using System.Globalization;

namespace Microsoft.Samples.SqlServer.Dts
{
    /// <summary>
    /// Base class that implements IDTSComponentUI interface and some common Data Flow OM specific routines.
    /// The purpose of IDTSComponentUI interface is to enable hanshaking of our UI with the designer diagram module.
    /// </summary>
    abstract class DataFlowComponentUI : IDtsComponentUI
    {
        #region Data members

        // entire communication with the components goes through these three interfaces
        private IDTSComponentMetaData100 componentMetadata;
        private IDTSDesigntimeComponent100 designtimeComponent;
        private IDTSVirtualInput100 virtualInput;

        // handy design-time services in case we need them
        private IServiceProvider serviceProvider;
        private IErrorCollectionService errorCollector;

        // some transforms are dealing with connections and/or variables
        private Connections connections;
        private Variables variables;

        #endregion

        #region Properties

        protected IDTSComponentMetaData100 ComponentMetadata
        {
            get
            {
                return this.componentMetadata;
            }
        }

        protected IDTSDesigntimeComponent100 DesigntimeComponent
        {
            get
            {
                return this.designtimeComponent;
            }
        }

        protected IDTSVirtualInput100 VirtualInput
        {
            get
            {
                return this.virtualInput;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        protected IServiceProvider ServiceProvider
        {
            get
            {
                return this.serviceProvider;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        protected Connections Connections
        {
            get
            {
                return this.connections;
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        protected Variables Variables
        {
            get
            {
                return this.variables;
            }
        }

        #endregion

        #region IDtsComponentUI Members

        // Called before Edit, New and Delete to pass in the necessary parameters. 
        void IDtsComponentUI.Initialize(Microsoft.SqlServer.Dts.Pipeline.Wrapper.IDTSComponentMetaData100 dtsComponentMetadata, IServiceProvider srvcProvider)
        {
            this.componentMetadata = dtsComponentMetadata;
            this.serviceProvider = srvcProvider;

            Debug.Assert(this.serviceProvider != null);

            this.errorCollector = this.serviceProvider.GetService(
                typeof(IErrorCollectionService)) as IErrorCollectionService;
            Debug.Assert(this.errorCollector != null);

            if (this.errorCollector == null)
            {
                Exception ex = new System.ApplicationException(Properties.Resources.NotAllEditingServicesAvailable);
                throw ex;
            }
        }

        // Called to invoke the UI.
        bool IDtsComponentUI.Edit(IWin32Window parentWindow, Microsoft.SqlServer.Dts.Runtime.Variables vars, Microsoft.SqlServer.Dts.Runtime.Connections conns)
        {
            ClearErrors();

            try
            {
                Debug.Assert(this.componentMetadata != null, "Original Component Metadata is not OK.");

                this.designtimeComponent = this.componentMetadata.Instantiate();

                Debug.Assert(this.designtimeComponent != null, "Design-time component object is not OK.");

                // Cache the virtual input so the available columns are easily accessible.
                this.LoadVirtualInput();

                // Cache variables and connections.
                this.variables = vars;
                this.connections = conns;

                // Here comes the UI that will be invoked in EditImpl virtual method.
                return EditImpl(parentWindow);
            }
            catch (Exception ex)
            {
                ReportErrors(ex);
                return false;
            }
        }

        // Called before adding the component to the diagram.
        void IDtsComponentUI.New(IWin32Window parentWindow)
        {
        }

        // Called before deleting the component from the diagram.
        void IDtsComponentUI.Delete(IWin32Window parentWindow)
        {
        }

        // Currently ignored.
        void IDtsComponentUI.Help(IWin32Window parentWindow)
        {
        }

        #endregion

        #region Handling errors

        // Clear the collection of errors collected by handling the pipeline events.
        protected void ClearErrors()
        {
            errorCollector.ClearErrors();
        }

        // Get the text of error message that consist of all errors captured from pipeline events (OnError and OnWarning).
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        protected string GetErrorMessage()
        {
            return errorCollector.GetErrorMessage();
        }

        /// <summary>
        /// Reports errors occurred in the components by retrieving 
        /// error messages reported through pipeline events
        /// </summary>
        /// <param name="ex"></param>
        protected void ReportErrors(Exception ex)
        {
            if (errorCollector.GetErrors().Count > 0)
            {
                MessageBox.Show(errorCollector.GetErrorMessage(), "Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Error,
                    MessageBoxDefaultButton.Button1, 0);
            }
            else
            {
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK,
                    MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
            }
        }

        #endregion

        #region Virtual methods

        // Bring up the form by implementing this method in subclasses.
        protected abstract bool EditImpl(IWin32Window parentControl);

        #endregion

        #region Handling virtual inputs

        /// <summary>
        /// Loads all virtual inputs and makes their columns easily accessible.
        /// </summary>
        protected void LoadVirtualInput()
        {
            Debug.Assert(this.componentMetadata != null);

            IDTSInputCollection100 inputCollection = componentMetadata.InputCollection;

            if (inputCollection.Count > 0)
            {
                IDTSInput100 input = inputCollection[0];
                this.virtualInput = input.GetVirtualInput();
            }
        }

        #endregion

        #region Helper methods

        /// <summary>
        ///  Returns component property for the property specified, null if no property exists 
        /// </summary>
        /// <param name="propertyCollection"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static IDTSCustomProperty100 GetCustomProperty(string propertyName, IDTSCustomPropertyCollection100 propertyCollection)
        {
            Debug.Assert(propertyCollection != null);

            for (int i = 0; i < propertyCollection.Count; i++)
            {
                IDTSCustomProperty100 propertyObject = propertyCollection[i];

                if (String.Compare(propertyObject.Name, propertyName, true,
                    System.Globalization.CultureInfo.InvariantCulture) == 0)
                {
                    return propertyObject;
                }
            }

            // Property not found... 
            return null;
        }

        /// <summary>
        /// Getting tooltip text to be displayed for the given data flow column.
        /// </summary>
        /// <param name="dataFlowColumn"></param>
        /// <returns></returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")]
        static public string GetTooltipString(object dataFlowColumn)
        {
            if (dataFlowColumn is IDTSVirtualInputColumn100)
            {
                IDTSVirtualInputColumn100 column = dataFlowColumn as IDTSVirtualInputColumn100;
                return FormatTooltipText(column.Name, column.DataType.ToString(),
                    column.Length.ToString(CultureInfo.CurrentUICulture),
                    column.Scale.ToString(CultureInfo.CurrentUICulture),
                    column.Precision.ToString(CultureInfo.CurrentUICulture),
                    column.CodePage.ToString(CultureInfo.CurrentUICulture),
                    column.SourceComponent);
            }
            else if (dataFlowColumn is IDTSInputColumn100)
            {
                IDTSInputColumn100 column = dataFlowColumn as IDTSInputColumn100;
                return FormatTooltipText(column.Name, column.DataType.ToString(),
                    column.Length.ToString(CultureInfo.CurrentUICulture),
                    column.Scale.ToString(CultureInfo.CurrentUICulture),
                    column.Precision.ToString(CultureInfo.CurrentUICulture),
                    column.CodePage.ToString(CultureInfo.CurrentUICulture));
            }
            else if (dataFlowColumn is IDTSOutputColumn100)
            {
                IDTSOutputColumn100 column = dataFlowColumn as IDTSOutputColumn100;
                return FormatTooltipText(column.Name, column.DataType.ToString(), column.Length.ToString(CultureInfo.CurrentUICulture),
                    column.Scale.ToString(CultureInfo.CurrentUICulture),
                    column.Precision.ToString(CultureInfo.CurrentUICulture),
                    column.CodePage.ToString(CultureInfo.CurrentUICulture));
            }
            else if (dataFlowColumn is IDTSExternalMetadataColumn100)
            {
                IDTSExternalMetadataColumn100 column = dataFlowColumn as IDTSExternalMetadataColumn100;
                return FormatTooltipText(column.Name, column.DataType.ToString(),
                    column.Length.ToString(CultureInfo.CurrentUICulture),
                    column.Scale.ToString(CultureInfo.CurrentUICulture),
                    column.Precision.ToString(CultureInfo.CurrentUICulture),
                    column.CodePage.ToString(CultureInfo.CurrentUICulture));
            }

            return string.Empty;
        }

        static public string FormatTooltipText(string name, string dataType, string length, string scale, string precision, string codePage, string sourceComponnet)
        {
            string tooltip = FormatTooltipText(name, dataType, length, scale, precision, codePage);
            tooltip += "\nSource: " + sourceComponnet;

            return tooltip;
        }

        static public string FormatTooltipText(string name, string dataType, string length, string scale, string precision, string codePage)
        {
            System.Text.StringBuilder strBuilder = new StringBuilder();
            strBuilder.Append("Name: ");
            strBuilder.Append(name);
            strBuilder.Append('\n');
            strBuilder.Append("Data type: ");
            strBuilder.Append(dataType);
            strBuilder.Append('\n');
            strBuilder.Append("Length: ");
            strBuilder.Append(length);
            strBuilder.Append('\n');
            strBuilder.Append("Scale: ");
            strBuilder.Append(scale);
            strBuilder.Append('\n');
            strBuilder.Append("Precision: ");
            strBuilder.Append(precision);
            strBuilder.Append('\n');
            strBuilder.Append("Code page: ");
            strBuilder.Append(codePage);

            return strBuilder.ToString();
        }

        #endregion
    }

    /// <summary>
    /// Used for comunication between a form and the controler object (...UI class).
    /// Name would be displayed in UI controls, but the actual object will be carried along in the Tag, 
    /// so it would not need to be searched for in collections when it comes back from the UI.
    /// It has implemented ToString() and GetHashCode() methods so it can be passed as a generic item to
    /// some UI controls (e.g. Combo Box) and used as a key in hash tables (if names are unique).
    /// Our grid wrappers allow storing these objects in grid cells as well.
    /// </summary>
    public class DataFlowElement
    {
        // name of the data flow object
        private string name;
        // reference to the actual data flow object
        private object tag;
        // tooltip to be displayed for this object
        private string toolTip;

        public DataFlowElement()
        {
        }

        // Sometimes it is handy to have string only objects.
        public DataFlowElement(string name)
        {
            this.name = name;
        }

        public DataFlowElement(string name, object tag)
        {
            this.name = name;
            this.tag = tag;
            this.toolTip = DataFlowComponentUI.GetTooltipString(tag);
        }

        public DataFlowElement Clone()
        {
            DataFlowElement newObject = new DataFlowElement();
            newObject.name = this.name;
            newObject.tag = this.tag;
            newObject.toolTip = this.toolTip;

            return newObject;
        }

        public string Name
        {
            get
            {
                return this.name;
            }
            set
            {
                this.name = value;
            }
        }

        public override string ToString()
        {
            return this.Name;
        }

        public override int GetHashCode()
        {
            return this.name.GetHashCode();
        }

        public object Tag
        {
            get { return this.tag; }
        }

        public string ToolTip
        {
            get { return this.toolTip; }
        }
    }
}
